﻿using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Input;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
using Avalonia.Rendering;
using voidbox;
using Silk.NET.OpenGLES;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using TTEngine.Graphics;
using TTEngine.Impl.Desktop;
using IGLES = Silk.NET.OpenGLES.GL;

namespace TTEngine.Impl.Avalonia
{
    public class GLMainView : OpenGlControlBase, IView, ICustomHitTest
    {
        public bool EditMode
        {
            get;
            private set;
        }
        public Vector2 Center
        {
            get
            {
                return new Vector2((this as ISize).Width / 2, (this as ISize).Height / 2);
            }
        }
        public bool HitTest(Point p)
        {
            return this.Bounds.Contains(p);
        }
        Impl_Window _implwin;
        public IGameWindow TryGetWindow()
        {
            return _implwin;
        }
        public GLMainView(Action OnInit,bool editormode)
        {
            this.EditMode= editormode;
            this._oninit = OnInit;
            this.RenderInfo = new RenderInfo();
        }
        public RenderInfo RenderInfo
        {
            get;
            private set;
        }
        Action _oninit;
        Silk.NET.OpenGLES.GL _gles;
        public IInput input
        {
            get
            {
                return _input;
            }
        }
        public GL GL => _gles;

        public float Scale => (float)this.VisualRoot.RenderScaling;

        public Action OnRender { get; set; }
        public Action<float> OnUpdate { get; set; }
        public Action<int, int> OnResize { get; set; }

        uint ISize.Width => (uint)(this.Bounds.Width * Scale);

        uint ISize.Height => (uint)(this.Bounds.Height * Scale);

        public Window GetWindow()
        {
            var p = this.Parent;

            while (p is not Window)
            {
                p = p.Parent;
            }
            return p as Window;
        }
        Impl_Input _input;
        bool inited = false;
        protected override void OnOpenGlInit(GlInterface gl)
        {
            //if (inited)
            //    return;
            //这个进第二次之后就不正常了
            base.OnOpenGlInit(gl);
            _gles = GL.GetApi(gl.GetProcAddress);
      
           

            TTEngine.API.platform = new Platform();
            TTEngine.API.audio = new Impl_Audio();
            TTEngine.API.loader = new Loader(EditMode);
            API.view = this;


            //TTEngine.API.mainwindow.ResizeAndCenter((int)(800 * Scale), (int)(600 * Scale));
            this._input = new Impl_Input();
            _gfxthreadid = System.Threading.Thread.CurrentThread.ManagedThreadId;
            _gfxrange = true;
            TTEngine.Runtime.activeGL = _gles;
            TTEngine.Runtime.activeTarget = this;
            _oninit.Invoke();
            TTEngine.Runtime.activeGL = null;
            TTEngine.Runtime.activeTarget = null;
            _gfxrange = false;
            var top = TopLevel.GetTopLevel(this);

            //var _win = GetWindow();
            if (top is Window win)
            {
                _implwin = new Impl_Window(win);

            }

            {
                top.KeyDown += (s, e) =>
                {
                    _OnKeyDown(e);
                };
                top.KeyUp += (s, e) =>
                {
                    _OnKeyUp(e);
                };
                top.TextInput += (s, e) =>
                {
                    _OnTextInput(e);
                };
            }

            this.PointerWheelChanged += (s, e) =>
            {
                this._input.ParseMouseWheel(this, (float)e.Delta.X, (float)e.Delta.Y);
            };

            this.PointerMoved += (s, e) =>
            {
                var p = e.GetCurrentPoint(this);
                int x = (int)p.Position.X;
                int y = (int)p.Position.Y;

                if (e.Pointer.Type == PointerType.Mouse)
                {
                    
                    if (p.Properties.IsLeftButtonPressed)
                        this._input.ParseMouse(this, MouseButton.Left, x, y, true);
                    if (p.Properties.IsMiddleButtonPressed)
                        this._input.ParseMouse(this, MouseButton.Middle, x, y, true);
                    if (p.Properties.IsRightButtonPressed)
                        this._input.ParseMouse(this, MouseButton.Right, x, y, true);
                    if(!p.Properties.IsLeftButtonPressed&&
                    !p.Properties.IsMiddleButtonPressed&&
                    !p.Properties.IsRightButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Unknown, x, y, false);
                    }
                }
                else
                {
                    //this._input.ParseTouch(this, p.Pointer.Id, x, y, true,true);
                }
            };
            this.PointerPressed += (s, e) =>
            {
                var p = e.GetCurrentPoint(this);
                int x = (int)p.Position.X;
                int y = (int)p.Position.Y;
                if (e.Pointer.Type == PointerType.Mouse)
                {
                    if (p.Properties.IsLeftButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Left, x, y, true);
                    }
                    if (p.Properties.IsMiddleButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Middle, x, y, true);
                    }
                    if (p.Properties.IsRightButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Right, x, y, true);
                    }
                }
                else
                {

                }
            };
            this.PointerReleased += (s, e) =>
            {
                var p = e.GetCurrentPoint(this);
                int x = (int)p.Position.X;
                int y = (int)p.Position.Y;
                if (e.Pointer.Type == PointerType.Mouse)
                {
                    if (!p.Properties.IsLeftButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Left, x, y, false);
                    }
                    if (!p.Properties.IsMiddleButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Middle, x, y, false);
                    }
                    if (!p.Properties.IsRightButtonPressed)
                    {
                        this._input.ParseMouse(this, MouseButton.Right, x, y, false);
                    }
                }
                else
                {

                }
            };
            inited = true;
        }


        int _thisfb;
        DateTime lasttime = DateTime.Now;
        protected override void OnOpenGlRender(GlInterface gl, int fb)
        {
            _thisfb = fb;
            //_gles.Viewport(0, 0, (uint)(this.Width* Scale), (uint)(this.Height*Scale));
            //_gles.ClearColor(1, 0.5f, 0.8f, 1);
            //_gles.Clear(ClearBufferMask.ColorBufferBit);
            _gfxrange = true;
            TTEngine.Runtime.activeGL = _gles;
            ParseGFXSafeCall();

            var ntim = DateTime.Now;
            float delta = (float)(ntim - lasttime).TotalSeconds;
            lasttime = ntim;

            if (delta > 0.1f)
                delta = 0.1f;
            this.OnUpdate?.Invoke(delta);
            this.OnRender?.Invoke();
            _gfxrange = false;
            TTEngine.Runtime.activeGL = null;
            _gles.Flush();
            this.RequestNextFrameRendering();
        }
        protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
        {
            //退出可见树的时候，不要清理
            //base.OnDetachedFromVisualTree(e);
        }
        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
        {
            base.OnAttachedToVisualTree(e);
        }
        protected override void OnOpenGlDeinit(GlInterface gl)
        {
            base.OnOpenGlDeinit(gl);
        }
        protected override void OnOpenGlLost()
        {
            base.OnOpenGlLost();
        }
        protected override void OnSizeChanged(SizeChangedEventArgs e)
        {
            base.OnSizeChanged(e);

            //防止单数尺寸
            //bool needresize = false;
            //int newheight = (int)e.NewSize.Height;
            //int newwidth = (int)e.NewSize.Width;
            //if (newheight % 2 == 1)
            //{
            //    this.GetWindow().Height = newheight - 1;
            //    needresize = true;
            //}
            //if (newwidth % 2 == 1)
            //{
            //    this.GetWindow().Width = newwidth - 1;
            //    needresize = true;
            //}
            //if (needresize)
            //{
            //    return;
            //}

            OnResize?.Invoke((int)(e.NewSize.Width * Scale), (int)(e.NewSize.Height * Scale));
        }
        bool _gfxrange;
        int _gfxthreadid;
        System.Collections.Concurrent.ConcurrentQueue<Action<IGLES>> safegfxqueue = new System.Collections.Concurrent.ConcurrentQueue<Action<IGLES>>();
        public void InvokeGfxThread(Action<IGLES> action)
        {
            if (System.Threading.Thread.CurrentThread.ManagedThreadId == _gfxthreadid && _gfxrange)
            {
                action(this.GL);
            }
            else
            {
                safegfxqueue.Enqueue(action);
            }
        }

        public async Task InvokeGfxThreadAsync(Action<IGLES> action)
        {
            bool finish = false;
            InvokeGfxThread((_gl) =>
            {
                action(_gl);
                finish = true;
            });
            while (!finish)
            {
                await Task.Delay(1);//等一帧，直到完成
            }
            return;
        }

        void ParseGFXSafeCall()
        {
            //执行GFX Action
            int ic = safegfxqueue.Count;
            for (var i = 0; i < ic; i++)
            {
                if (safegfxqueue.TryDequeue(out Action<IGLES> action))
                {
                    action(GL);
                }
            }
        }


        public bool IsMainOutput()
        {
            return true;
        }

        public void Apply()
        {
            Runtime.activeTarget = this;
            _gles.BindFramebuffer(FramebufferTarget.Framebuffer, (uint)this._thisfb);
            ISize size = this;
            var w = size.Width;
            var h = size.Height;
            _gles.Viewport(0, 0,w, h);
        }

        public void Clear(Color color)
        {
            _gles.ClearColor(color.r, color.g, color.b, color.a);
            _gles.DepthMask(true);
            _gles.ClearDepth(1.0f);
            _gles.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        }

        void _OnKeyDown(KeyEventArgs e)
        {
            this._input.ParseKey(this, KeyCodeConverter.Conv(e.Key), true);
        }
        void _OnKeyUp(KeyEventArgs e)
        {
            this._input.ParseKey(this, KeyCodeConverter.Conv(e.Key), false);
        }
        void _OnTextInput(TextInputEventArgs e)
        {
            this._input.ParseInputText(this, e.Text);
        }

    }
}
